﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Linq.SqlClient;
using t_205_verk_hopur_12.Helpers;

namespace t_205_verk_hopur_12.Models
{
    public class ChannelRepository : t_205_verk_hopur_12.Models.IChannelRepository 
    {
        public MittTVDataContext db = new MittTVDataContext();
        UserRepository userRepo = new UserRepository();
        VideoRepository videoRepo = new VideoRepository();
        Tool toolkit = new Tool();

        // To add new channel
        public void Add(Channel newChannel)
        {
            db.Channels.InsertOnSubmit(newChannel);
        }

        // Saving changes to database, Persistence
        public void Save()
        {
            db.SubmitChanges();
        }

        // To select all the channels by rating
        // Maybe should be changed to return only a set number of channels
        public IQueryable<Channel> GetHighestRated()
        {
            return from channel in db.Channels
                   orderby channel.Rating descending
                   select channel;                   
        }

        // To select the top 4 channels you own
        public IQueryable<Channel> GetTop4Channels()
        {
            return (from channel in db.Channels
                    orderby channel.Rating descending
                    select channel).Take(4);
        }

        // To select the top 4 channels you own
        public IQueryable<Channel> GetTop4ChannelsYouOwn(int userID)
        {
            return (from channel in db.Channels
                    where channel.OwnerID == userID
                    orderby channel.Rating descending
                    select channel).Take(4);
        }

        // To select the top 4 channels
        public IQueryable<Channel> GetTop4SubscribedChannels(int userID)
        {
            return (from channel in db.Channels
                    join subscription in db.Subscriptions on channel.ID equals subscription.ChannelID
                    where subscription.UserID == userID
                    orderby channel.Rating descending
                    select channel).Take(4);
        }

        // To get a specific channel
        public Channel GetChannelByID(int ID)
        {
            return (from channel in db.Channels
                    where channel.ID == ID
                    select channel).SingleOrDefault();
        }

        

        // To get the announcements on a channel
        // Maybe should change it to return only a set amount of announcements
        public IQueryable<Announcement> GetAnnouncementsByID(int channelID)
        {
            return from announcement in db.Announcements
                   where announcement.ChannelID == channelID
                   select announcement;
        }

        // For adding a new announcement(notice) in database
        public void AddAnnouncements(Announcement newAnn)
        {
            db.Announcements.InsertOnSubmit(newAnn);
        }

        // Get an announcement by id
        public Announcement GetAnnouncementByAnnouncementID(int id)
        {
            return (from announcement in db.Announcements
                   where announcement.ID == id
                   select announcement).SingleOrDefault();
        }

        // To get all the comments on a particular channel
        public IQueryable<ChannelComment> GetChannelComentsByID(int channelID)
        {
            return from comment in db.ChannelComments
                   where comment.ChannelID == channelID
                   select comment;
        }

        // To subscribe to a particular channel
        public void SubscribeToChannel(Subscription newSub)
        {
            db.Subscriptions.InsertOnSubmit(newSub);              
        }

        // Checks if a subscriber is already subscribed
        public bool IsSubscribed(int userID, int channelID)
        {
            return (from subscription in db.Subscriptions
                    where subscription.ChannelID == channelID && subscription.UserID == userID
                    select subscription).Count() >= 1;
        }

        // Check if a user is an employee of a channel
        public bool IsEmployee(int userID, int channelID)
        {
            return (from p in db.Permissions
                    where (p.ChannelID == channelID && p.UserID == userID)
                    select p).Count() >= 1;
        }

        // To vote on a particular channel
        public void VoteChannel(int channelID, int vote)
        {
            ChannelVote channelVote = new ChannelVote();
            
            Channel channel = GetChannelByID(channelID);
            channel.Rating += vote;

            var currentUser = toolkit.GetCurrentUser();
            
            channelVote.ChannelID = channelID;
            channelVote.UserId = currentUser.ID;
            channelVote.Vote = vote;

            db.ChannelVotes.InsertOnSubmit(channelVote);

            Save();
        }

        // Check if the user has voted for a channel
        public bool HasUserVotedChannel(ChannelVote channelVote)
        {
            return (from cv in db.ChannelVotes
                    where cv == channelVote
                    select cv).Count() >= 1;
        }

        // Check if the user is an employee or owner of a channel
        public bool IsOwnerOrEmployee(int userID, int channelID)
        {
            Channel channel = GetChannelByID(channelID);

            // The user is not the owner, check if he is an employee
            if (channel.OwnerID != userID && !IsEmployee(userID, channelID))
            {
                return false;
            }

            return true;
        }

        // To vote on a particular video
        public void VoteVideo(int userID, int videoID, int vote)
        {
            VideoVote videoVote = new VideoVote();
           
            videoVote.VideoID = videoID;
            videoVote.Vote = vote;

            db.VideoVotes.InsertOnSubmit(videoVote);
            Save();
        }

        // Adds a video to election
        public void AddVideoToElection(VideoElection newElection)
        {
            db.VideoElections.InsertOnSubmit(newElection);
            SetVideoStatus(newElection.VideoID, 1);
        }

        // Ends a single election
        public void EndElection(int channelID, int videoID)
        {
            VideoElection endElection = (from e in db.VideoElections
                                         where e.VideoID == videoID && e.ChannelID == channelID
                                         && e.HasEnded != true
                                         select e).SingleOrDefault();

            endElection.HasEnded = true;
            Save();
            
            if (endElection.Rating < 0)
            {
                SetVideoStatus(videoID, 3);
            }
            else
            {
                SetVideoStatus(videoID, 2);
            }
        }

        // Sets the status of video to election state
        public void SetVideoStatus(int videoID, int state)
        {
            Video stateVideo = (from v in db.Videos
                                where v.ID == videoID
                                select v).SingleOrDefault();

            stateVideo.State = state;
            Save();
        }

        // Getting all elections on channel
        public IQueryable<VideoElection> GetElectionsByChannelId(int channelID, int userID)
        {
            return from election in db.VideoElections
                   where election.ChannelID == channelID && 
                         election.HasEnded != true &&
                         election.EndTime > DateTime.Now &&
                         !(from votes in db.VideoElectionVotes 
                           where votes.UserID == userID
                           select votes.ElectionID).Contains(election.ID)
                   select election;
        }

        // Returns all video that are in election state
        public IQueryable<Video> GetVideoInElectionState(int channelID)
        {
            return from ve in db.VideoElections
                   join v in db.Videos on ve.VideoID equals v.ID into VidElect
                   where ve.ChannelID == channelID && ve.HasEnded == false
                   select ve.Video;
        }

        // Updates the election and logs who voted on what!!
        public void SaveElectionByUserId(int userID, int key, int value)
        {
            VideoElectionVote newVote = new VideoElectionVote();
            newVote.ElectionID = key;
            newVote.UserID = userID;
            newVote.Vote = value;

            db.VideoElectionVotes.InsertOnSubmit(newVote);

            Save();

            UpdateVideoElectionRating(key, value);
           
        }

        // Updates the rating after user has voted
        public void UpdateVideoElectionRating(int electionId, int value)
        {
            VideoElection updateRating = new VideoElection();
            updateRating = (from r in db.VideoElections
                           where r.ID == electionId
                           select r).Single();
            updateRating.Rating = updateRating.Rating + value;
            Save();
        }


        // To get all channels that match the string s in name or description
        public IEnumerable<ChannelSearchViewModel> GetSearchResults(string query, string sort, string by)
        {
            // Join to get the category names and put into a view model for simpler output
            // Searches with the like method since users don't know what they are looking for exactly
            var result = from channel in db.Channels
                         where SqlMethods.Like(channel.Name, "%" + query + "%")
                            || SqlMethods.Like(channel.Description, "%" + query + "%")
                         join category in db.Categories on channel.CategoryID equals category.ID into catChannel
                         orderby channel.Name ascending
                         select new ChannelSearchViewModel
                         {
                             Channel = channel,
                             Category = channel.Category
                         };

            // Order by popularity
            if(sort == "popular")
            {
                if(by == "desc")
                {
                    result = result.OrderByDescending(attr => attr.Channel.Rating);
                }
                else
                {
                    result = result.OrderBy(attr => attr.Channel.Rating);
                }
            }

            // Order by name
            if (sort == "name")
            {
                if (by == "desc")
                {
                    result = result.OrderByDescending(attr => attr.Channel.Name);
                }
                else
                {
                    result = result.OrderBy(attr => attr.Channel.Name);
                }
            }

            // Order by category name
            if (sort == "category")
            {
                if (by == "desc")
                {
                    result = result.OrderByDescending(attr => attr.Category.Name);
                }
                else
                {
                    result = result.OrderBy(attr => attr.Category.Name);
                }
            }

            return result;
        }

        public Subscription GetSubscriptionsByUserAndChannel(int userID, int channelID)
        {
            return (from sub in db.Subscriptions
                    where sub.UserID == userID && sub.ChannelID == channelID
                    select sub).SingleOrDefault();
        }

        public void UnsubscribeToChannel(Subscription sub)
        {
            db.Subscriptions.DeleteOnSubmit(sub);
        }

        public bool HasApplied(int userID, int channelID)
        {
            return (from app in db.Applications
                    where app.ChannelID == channelID && app.UserID == userID
                    select app).Count() >= 1;
        }

        public void ApplyAtChannel(Application newApp)
        {
            db.Applications.InsertOnSubmit(newApp);
        }

        public IQueryable<User> GetApplicationsByChannelID(int channelID)
        {
            return (from c in db.Users
                    join s in db.Applications on c.ID equals s.UserID
                    where s.ChannelID == channelID
                    select c);
        }

        public Application GetApplicationByUserIdAndChannelId(int userID, int channelID)
        {
            return (from a in db.Applications
                    where a.UserID == userID && a.ChannelID == channelID
                    select a).SingleOrDefault();
        }

        public void RemoveApplication(Application app)
        {
            db.Applications.DeleteOnSubmit(app);
        }

        public void HireEmployee(Permission pm)
        {
            db.Permissions.InsertOnSubmit(pm);
        }

        public IEnumerable<User> GetEmployeesByChannelId(int channelID)
        {
            return (from c in db.Users
                    join s in db.Permissions on c.ID equals s.UserID
                    where s.ChannelID == channelID
                    select c);
        }

        public Permission GetPermissionByUserIdAndChannelId(int userID, int channelID)
        {
            return (from c in db.Users
                    join s in db.Permissions on c.ID equals s.UserID
                    where s.ChannelID == channelID && s.UserID == userID
                    select s).SingleOrDefault();
        }

        public void RevokePermission(Permission pm)
        {
            db.Permissions.DeleteOnSubmit(pm);
        }
    }
}
